/*
 *MIT License
 *
 *Copyright (c) 2019 chenshuai cs4380@163.com
 *
 *Permission is hereby granted, free of charge, to any person obtaining a copy
 *of this software and associated documentation files (the "Software"), to deal
 *in the Software without restriction, including without limitation the rights
 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *copies of the Software, and to permit persons to whom the Software is
 *furnished to do so, subject to the following conditions:
 *
 *The above copyright notice and this permission notice shall be included in all
 *copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *SOFTWARE.
 */

package com.cs.cslc.common.biz;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cs.cslc.common.mapper.SuperMapper;
import com.cs.cslc.common.msg.TableResult;
import com.cs.cslc.common.pojo.ParamQuery;
import com.cs.cslc.common.util.BeanUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * BaseBusinessBiz 基础业务类.
 * <p>
 * 新增和修改时自动添加: 创建时间、创建人、更新时间、更新人.
 * 泛型：M 是 mapper 对象，T 是实体.
 * 说明：
 * 使用mybatis-plus，单表操作时通过Wrapper构造生成查询对象，
 * 此类提供常用的方法，通过Model构造生成查询对象。
 * </p>
 *
 * @author cs4380 https://gitee.com/xhbug_cs4380  cs4380@163.com
 * @version 1.0
 * @since 2019-09-28 21:25
 */
public class BaseBusinessBiz<M extends SuperMapper<T>, T> extends ServiceImpl<M, T> {

    /**
     * 通过实体，新增单条数据
     * <p>
     * 保存一个实体，null的属性不会保存，会使用数据库默认值
     * </p>
     *
     * @param entity 实体
     */
    public boolean insertModel(T entity) {
        BeanUtil.beanAttributeValueTrim(entity);
        return super.save(entity);
    }

    /**
     * 通过实体，更新单条数据
     * <p>
     * 根据主键更新实体全部字段，null值不会更新.
     * 需要更新时，添加在实体类添加
     * TableField(strategy = FieldStrategy.IGNORED)
     * </p>
     *
     * @param entity 实体，包含id
     */
    @Override
    public boolean updateById(T entity) {
        BeanUtil.beanAttributeValueTrim(entity);
        return super.updateById(entity);
    }

    /**
     * 通过实体，逻辑删除单条数据
     *
     * @param id 主键
     */
    public boolean removeById(Serializable id) {
        return super.removeById(id);
    }

    /**
     * 获取全部数据
     * <p>
     * 注意：如果当前实体中存在isDeleted字段，默认添加未删除的条件
     * </P>
     *
     * @param model 查询条件,为 null 则查询所有
     */
    public List<T> selectModelList(T model) {
        QueryWrapper<T> queryWrapper = this.modelBuildQueryWrapper(model);
        return super.baseMapper.selectList(queryWrapper);
    }

    /**
     * 通过实体条件，统计数量
     * <p>
     * 根据实体中的属性查询总数，查询条件使用等号
     * </p>
     *
     * @param model 查询实体
     * @return 匹配总数
     */
    public Long selectCount(T model) {
        QueryWrapper<T> queryWrapper = this.modelBuildQueryWrapper(model);
        return super.baseMapper.selectCount(queryWrapper);
    }

    /**
     * 通过查询参数，获取翻页数据
     *
     * @param query 查询参数对象
     */
    public TableResult<T> selectTableByParamQuery(ParamQuery query) {
        return this.selectTableByParamQuery(query, null);
    }

    /**
     * 通过查询参数，获取翻页数据
     * <p>
     * 需要表中存在create_time字段
     * </p>
     *
     * @param query   查询参数对象
     * @param columns 待查询的sql字段
     * @return 匹配数据
     */
    public TableResult<T> selectTableByParamQuery(ParamQuery query, String... columns) {
        QueryWrapper<T> queryWrapper = Wrappers.query();
        queryParam(queryWrapper, query);
        // 添加排序字段
        if (null != columns) {
            queryWrapper.select(columns);
        }
        // 默认创建日期倒叙
        queryWrapper.orderByDesc("create_time");
        IPage<T> page = this.baseMapper.selectPage(new Page<T>(query.getPage(), query.getLimit()), queryWrapper);
        List<T> list = page.getRecords();
        if (BeanUtil.isEmpty(list)) {
            return new TableResult<>(0, new ArrayList<>());
        }
        return new TableResult<>(page.getTotal(), list);
    }

    /**
     * 获取全部数据
     * <p>
     * 注意：如果当前实体中存在isDeleted字段，默认添加未删除的条件
     * </P>
     *
     * @param params 查询条件,为 null 则查询所有
     */
    public List<T> selectListAll(ParamQuery params) {
        return this.selectFieldListAllDesc(params, null, null);
    }

    /**
     * 获取指定字段结果集多条数据
     * <p>
     * 注意：如果当前实体中存在isDeleted字段，默认添加未删除的条件
     * </p>
     *
     * @param query         查询条件,为 null 则查询所有非逻辑删除的数据
     * @param fields        实体属性名数组，为null则查询所有
     * @param orderByClause 排序字段sql语句.案例："create_time"
     * @return 匹配数据
     */
    public List<T> selectFieldListAllDesc(ParamQuery query, String[] fields, String orderByClause) {
        QueryWrapper<T> queryWrapper = Wrappers.query();
        if (BeanUtil.isNotEmpty(fields)) {
            queryWrapper.select(fields);
        }
        if (StringUtils.isNotBlank(orderByClause)) {
            queryWrapper.orderByDesc(orderByClause);
        }
        this.queryParam(queryWrapper, query);
        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 获取指定字段结果集多条数据
     * <p>
     * 注意：如果当前实体中存在isDeleted字段，默认添加未删除的条件
     * </p>
     *
     * @param query         查询条件,为 null 则查询所有非逻辑删除的数据
     * @param fields        实体属性名数组，为null则查询所有
     * @param orderByClause 排序字段sql语句.案例："create_time"
     * @return 匹配数据
     */
    public List<T> selectFieldListAllAsc(ParamQuery query, String[] fields, String orderByClause) {
        QueryWrapper<T> queryWrapper = Wrappers.query();
        if (BeanUtil.isNotEmpty(fields)) {
            queryWrapper.select(fields);
        }
        if (StringUtils.isNotBlank(orderByClause)) {
            queryWrapper.orderByAsc(orderByClause);
        }
        this.queryParam(queryWrapper, query);
        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 通过ParamQuery，设置给queryWrapper查询条件
     * <p>
     * 查询条件都是模糊查询
     * </p>
     *
     * @param queryWrapper 查询对象
     * @param params       查询条件
     */
    public void queryParam(QueryWrapper<T> queryWrapper, ParamQuery params) {
        // 设置查询参数
        if (BeanUtil.isNotEmpty(params)) {
            Object value;
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                value = entry.getValue();
                String key = this.toLowerCase(entry.getKey());
                // 字符串则做模糊匹配
                if (value instanceof String) {
                    queryWrapper.like(key, entry.getValue().toString());
                } else {
                    queryWrapper.eq(key, entry.getValue().toString());
                }
            }
        }
    }

    /**
     * 驼峰字段名转换为数据库字段
     *
     * @param fieldName 驼峰字段名
     * @return 数据库字段
     */
    private String toLowerCase(String fieldName) {
        StringBuilder str = new StringBuilder();
        char[] fieldNames = fieldName.toCharArray();
        for (char field : fieldNames) {
            if (Character.isUpperCase(field)) {
                str.append("_");
            }
            str.append(field);
        }
        return str.toString().toLowerCase();
    }

    /**
     * 通过实体对象构建查询对象
     *
     * @param model 实体对象
     * @return QueryWrapper 查询对象
     */
    private QueryWrapper<T> modelBuildQueryWrapper(T model) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        try {
            Class clazz = model.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 获取属性的名字
                String name = field.getName();
                if (Modifier.isStatic(field.getModifiers())) {
                    // 跳过所有静态属性
                    continue;
                }
                // 私有属性必须设置访问权限
                field.setAccessible(true);
                // 将属性名字的首字母大写
                String filedName = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
                // 获取属性 get 方法
                Method method = clazz.getMethod("get" + filedName);
                // 调用get方法获取属性值
                Object resultValue = method.invoke(model);
                if (null != resultValue) {
                    queryWrapper.eq(this.toLowerCase(name), resultValue);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return queryWrapper;
    }
}